class ListNode {
    constructor(id) {
        this.pre = null;
        this.next = null;
        this.id = id;
    }
  };
  
  class LRUCache {
    constructor(maxLength, getDataCb, name) {
      this.maxLength = maxLength;
      this.getDataCb = getDataCb;
      this.name = name;
  
      this.length = 0;
      this.listHead = null;
      this.listTail = null;
      this.listNode = {};
      this.cache = {};
  
      this.hit = 0;
      this.query = 0;
    }
  
    async getData(id) {
      do {
        if (this.cache[id]) {
          ++this.hit;
          break;
        }
        let data = await this.getDataCb(id);
        this.cache[id] = data;
        break;
      } while (false);
      ++this.query;
      this.updateCache(id);
      this.static();
      return this.cache[id];
    }
  
    updateCache(id) {
      let node = this.listNode[id];
      if (node) {
        if (this.listHead == this.listTail)
          return;
        if (this.listHead == node)
          return;
        if (!node.next) {
          node.pre.next = null;
          this.listTail = node.pre;
          node.next = this.listHead;
          node.next.pre = node;
          this.listHead = node;
          return;
        }
        node.pre.next = node.next;
        node.next.pre = node.pre;
        node.next = this.listHead;
        node.next.pre = node;
        this.listHead = node;
      } else {
        let newNode = new ListNode(id);
        newNode.next = this.listHead;
        this.listHead = newNode;
        if (!newNode.next)
          this.listTail = newNode;
        else
          newNode.next.pre = newNode;
        this.listNode[id] = newNode;
        ++this.length;
        //console.log("add id---------->", id);
      }
      this.delCache();
    }
  
    delTail() {
      let node = this.listTail;
      this.listTail = node.pre;
      this.listTail.next = null;
      --this.length;
      delete this.listNode[node.id];
      delete this.cache[node.id];
      //console.log("delete id--------->", node.id);
    }
  
    delCache() {
      while (this.length > this.maxLength) {
        this.delTail();
      }
    }
  
    dump() {
      let head = this.listHead;
      let dump = [];
      while (head) {
        dump.push(head.id);
        head = head.next;
      }
      console.log("dump ------", ...dump);
    }
  
    static() {
      if (0 == this.query % (10 * this.maxLength)) {
        console.log(this.query, this.name, "hitRate", this.hit / this.query);
      }
    }
  };